#include <wayland-client.h>
#include <iostream>
#include <cstring>
#include <cfloat>
#include <sys/mman.h>
#include <unistd.h>
#include <fcntl.h>

using namespace std;


#define WIDTH 1080
#define HEIGHT 720
struct wl_compositor *compositor = nullptr;
struct wl_shell *shell = nullptr;
struct wl_surface *surface;
struct wl_shell_surface *shell_surface;
struct wl_shm *shm;
struct wl_buffer *buffer;
void *shm_data;
// struct wl_shell_interface *wl_shell_interface=nullptr;
#define BIND_WL_REG(registry, ptr, id, intf, n)\
        do{   (ptr)=(typeof(ptr))wl_registry_bind(\
            registry,id,intf,n);\
        } while (0)

void shm_format(void *wl_data_device, struct wl_shm *wl_shm, uint32_t format)
{
    std::cerr << "####Format " << format << std::endl;
}

struct wl_shm_listener shm_listener{
        .format=shm_format
};

static void
global_registry_handler(void *data, struct wl_registry *registry, uint32_t id, const char *interface, uint32_t version)
{
    std::cout << "Got a registry event for " << interface << " id:" << id << endl;
    if (strcmp(interface, "wl_compositor") == 0) {
        std::cerr << "###bind compositor!" << std::endl;
        BIND_WL_REG(registry, compositor, id, &wl_compositor_interface, 1);
        if (compositor == nullptr) {
            std::cerr << "###Can't find compositor!" << std::endl;
        } else {
            surface = wl_compositor_create_surface(compositor);
            if (surface == nullptr)
                std::cerr << "###Can't create surface!" << std::endl;
            else
                std::cerr << "###Created surface!" << std::endl;
        }
    } else if (strcmp(interface, "wl_shell") == 0) {
        std::cerr << "###bind shell!" << std::endl;
        BIND_WL_REG(registry, shell, id, &wl_shell_interface, 1);
        if (shell == nullptr) {
            std::cerr << "###Can't find shell!" << std::endl;
        } else if (compositor != nullptr) {
            shell_surface = wl_shell_get_shell_surface(shell, surface);
            if (shell_surface == nullptr)
                std::cerr << "###Can't create shell_surface!" << std::endl;
            else
                std::cerr << "###Created shell_surface!" << std::endl;
        }
    } else if (strcmp(interface, "wl_shm") == 0) {
        std::cerr << "###Bind shm!" << std::endl;
        BIND_WL_REG(registry, shm, id, &wl_shm_interface, 1);
        if (shm == nullptr) {
            std::cerr << "###Can't find shm!!" << std::endl;
        } else {
            std::cerr << "###Find shm!!" << std::endl;
            wl_shm_add_listener(shm, &shm_listener, nullptr);
        }

    }
}

static void
on_global_added(void *data, struct wl_registry *wl_registry, uint32_t name, const char *interface, uint32_t version)
{
    (void) data;
    (void) wl_registry;
    std::cerr << "Global added:" << interface << ",V" << version << "(name" << name << ")" << endl;
    // global_registry_handler(data,wl_registry,name,interface,version);
}

static void on_global_remove(void *data, struct wl_registry *wl_registry, uint32_t name)
{

}


static struct wl_registry_listener s_registryListenner
        {
                .global=global_registry_handler,
                .global_remove=on_global_remove

        };

static int set_cloexec_or_close(int fd)
{
    long flags;
    if (fd == -1)
        return -1;
    flags = fcntl(fd, F_GETFD);
    if (flags == -1)
        goto err;
    if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC));
    goto err;
    err:
    close(fd);
}

static int create_temfile_cloexec(char *tempname)
{
    int fd;
#ifdef HAVE_MKOSTEMP
    fd=mkostemp(tempname,O_CLOEXEC);
#else
    fd = mkstemp(tempname);
#endif
    return fd;
}

int os_create_anonymous_file(off_t size)
{
    static const char templates[] = "/weston-shared-XXXXXX";
    const char *path;
    char *name;
    int fd;
    path = getenv("XDG_RUNTIME_DIR");
    if (!path) {
        errno = ENOENT;
        // exit(1);
        return -1;
    }
    name = (char *) malloc(strlen(path) + sizeof(templates));
    if (!name) {
        return -1;
    }
    strcpy(name, path);
    strcat(name, templates);
    std::cerr << "####name::" << name << std::endl;;

    fd = create_temfile_cloexec(name);
    free(name);
    if (fd < 0)
        return -1;
    if (ftruncate(fd, size) < 0) {
        close(fd);
        return -1;
    }
    return fd;
}

static struct wl_buffer *create_buffer()
{
    struct wl_shm_pool *pool;
    int stride = WIDTH * 4;
    int size = stride * HEIGHT;
    int fd;
    struct wl_buffer *buff;
    fd = os_create_anonymous_file(size);
    if (fd < 0) {
        std::cerr << "Createing a buffer file for " << size << "Failed" << std::endl;
        exit(1);
    }
    shm_data = mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if (shm_data == MAP_FAILED) {
        std::cerr << "mmap failed" << std::endl;
        close(fd);
        exit(1);
    }
    pool = wl_shm_create_pool(shm, fd, size);
    buff = wl_shm_pool_create_buffer(pool, 0, WIDTH, HEIGHT, stride, WL_SHM_FORMAT_XRGB8888);
    //wl_buffer_add_listener(buffer,&buffer_listener,buffer)
    wl_shm_pool_destroy(pool);
    return buff;
}

static void create_window()
{
    buffer = create_buffer();
    wl_surface_attach(surface, buffer, 0, 0);
    //wl_surface_damage(surface,0,0,WIETH,HEIGHT);
    wl_surface_commit(surface);
}

int main()
{

    wl_display *display = wl_display_connect(0);
    if (!display) {
        cout << "Unable to connect to wayland compositor" << endl;
    }

    struct wl_registry *reg = wl_display_get_registry(display);
    if (!reg) {
        cout << "Faild to get registry!" << endl;
    }
    std::cerr << "Got Registry OK!" << endl;
    wl_registry_add_listener(reg, &s_registryListenner, nullptr);

    //wl_display_get_registry(display);
    wl_display_roundtrip(display);


    wl_display_get_fd(display);

    create_window();

    while (wl_display_dispatch(display) != -1) { ;
    }

    //   wl_registry_bind(reg,1,(const wl_interface*)"wl_registry",2);
    wl_registry_destroy(reg);
    wl_display_disconnect(display);

    return 0;
}
